﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework.Input;

namespace Skyhook.Core.Input
{
    public static class InputEvents
    {
        public delegate void ConcreteKeyEvent();
        public delegate void OnKeyEvent(Keys key);
        public delegate void OnMouseEvent(int x, int y);

        public static event OnKeyEvent KeyPressed;
        public static event OnKeyEvent KeyDown;
        public static event OnMouseEvent MouseMove;
        public static event OnMouseEvent MouseLeftDown;
        public static event OnMouseEvent MouseLeftClick;
        public static event OnMouseEvent MouseRightDown;
        public static event OnMouseEvent MouseRightClick;

        static bool[] Keys = new bool[256];
        static MouseState LastMouseState;

        static Dictionary<Keys, List<ConcreteKeyEvent>> KeyDownSubscriptions = new Dictionary<Keys, List<ConcreteKeyEvent>>();
        static Dictionary<Keys, List<ConcreteKeyEvent>> KeyPressedSubscriptions = new Dictionary<Keys, List<ConcreteKeyEvent>>();

        static InputEvents()
        {
            FillKeysWithFalse();
            ConnectEmptyInput();
        }

        static void FillKeysWithFalse()
        {
            for (int i = 0; i < Keys.Length; i++)
            {
                Keys[i] = false;
            }
        }

        static void ConnectEmptyInput()
        {
            KeyPressed += EmptyKeyEvent;
            KeyDown += EmptyKeyEvent;
            MouseLeftDown += EmptyMouseEvent;
            MouseLeftClick += EmptyMouseEvent;
            MouseRightClick += EmptyMouseEvent;
            MouseRightDown += EmptyMouseEvent;
            MouseMove += EmptyMouseEvent;
        }

        static void EmptyKeyEvent(Keys key)
        {

        }

        static void EmptyMouseEvent(int x, int y)
        {

        }

        public static void Update()
        {
            UpdateKeyboard();
            UpdateMouse();            
        }

        static void UpdateKeyboard()
        {
            KeyboardState keystate = Keyboard.GetState();

            for (int i = 0; i < Keys.Length; i++)
            {
                CheckKeyDown(keystate, i);
                CheckKeyPressed(keystate, i);
                
                Keys[i] = keystate.IsKeyDown((Keys)i);
            }
        }

        static void CheckKeyDown(KeyboardState keystate, int keypos)
        {
            if (keystate.IsKeyDown((Keys)keypos))
            {
                KeyDown((Keys)keypos);
                CheckSubscriptions((Keys)keypos, KeyDownSubscriptions);
            }
        }
        static void CheckKeyPressed(KeyboardState keystate, int keypos)
        {
            if (Keys[keypos] && keystate.IsKeyUp((Keys)keypos))
            {
                KeyPressed((Keys)keypos);
                CheckSubscriptions((Keys)keypos, KeyPressedSubscriptions);
            }
        }

        static void CheckSubscriptions(Keys key, Dictionary<Keys, List<ConcreteKeyEvent>> dictionary)
        {
            List<ConcreteKeyEvent> events = null;
            if (dictionary.TryGetValue(key, out events))
            {
                foreach (ConcreteKeyEvent keyevent in events)
                {
                    keyevent();
                }
            }
        }

        static void UpdateMouse()
        {
            MouseState mousestate = Mouse.GetState();

            CheckMouseMove(mousestate);
            CheckMouseLeftDown(mousestate);
            CheckMouseLeftClick(mousestate);
            CheckMouseRightDown(mousestate);
            CheckMouseRightClick(mousestate);            

            LastMouseState = mousestate;
        }

        static void CheckMouseMove(MouseState mousestate)
        {
            if (mousestate.X != LastMouseState.X || mousestate.Y != LastMouseState.Y)
            {
                MouseMove(mousestate.X, mousestate.Y);
            }
        }
        static void CheckMouseLeftDown(MouseState mousestate)
        {
            if (mousestate.LeftButton == ButtonState.Pressed)
            {
                MouseLeftDown(mousestate.X, mousestate.Y);
            }
        }
        static void CheckMouseLeftClick(MouseState mousestate)
        {
            if (mousestate.LeftButton == ButtonState.Released && LastMouseState.LeftButton == ButtonState.Pressed)
            {
                MouseLeftClick(mousestate.X, mousestate.Y);
            }
        }
        static void CheckMouseRightDown(MouseState mousestate)
        {
            if (mousestate.RightButton == ButtonState.Pressed)
            {
                MouseRightDown(mousestate.X, mousestate.Y);
            }
        }
        static void CheckMouseRightClick(MouseState mousestate)
        {
            if (mousestate.RightButton == ButtonState.Released && LastMouseState.RightButton == ButtonState.Pressed)
            {
                MouseRightClick(mousestate.X, mousestate.Y);
            }
        }

        public static void SubscribeOnConcreteKeyDown(Keys key, ConcreteKeyEvent keyevent)
        {
            SubscribeOnConcreteKey(key, keyevent, KeyDownSubscriptions);
        }
        public static void SubscribeOnConcreteKeyPressed(Keys key, ConcreteKeyEvent keyevent)
        {
            SubscribeOnConcreteKey(key, keyevent, KeyPressedSubscriptions);
        }

        static void SubscribeOnConcreteKey(Keys key, ConcreteKeyEvent keyevent, Dictionary<Keys, List<ConcreteKeyEvent>> dictionary)
        {
            List<ConcreteKeyEvent> events = null;
            if (dictionary.TryGetValue(key, out events))
            {
                events.Add(keyevent);
            }
            else
            {
                events = new List<ConcreteKeyEvent>();
                dictionary.Add(key, events);
                events.Add(keyevent);
            }
        }

        public static void UnsubscribeOnConcreteKeyDown(Keys key, ConcreteKeyEvent keyevent)
        {
            UnsubscribeOnConcreteKey(key, keyevent, KeyDownSubscriptions);
        }
        public static void UnsubscribeOnConcreteKeyPressed(Keys key, ConcreteKeyEvent keyevent)
        {
            UnsubscribeOnConcreteKey(key, keyevent, KeyPressedSubscriptions);
        }

        static void UnsubscribeOnConcreteKey(Keys key, ConcreteKeyEvent keyevent, Dictionary<Keys, List<ConcreteKeyEvent>> dictionary)
        {
            List<ConcreteKeyEvent> events = null;
            if (dictionary.TryGetValue(key, out events))
            {
                events.Remove(keyevent);
            }
        }
    }
}
